/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include <Carbon/Carbon.h>
#include "Creator.h"
#include "Hardware.h"
#include <lib3d/hard/Manager.h>
#include <logs/Log.h>
#include <vector>

namespace liba
{
namespace lib3d
{
namespace hard
{
namespace macx
{

static std::vector<Hardware *> * hardwares;

CFBundleRef gBundleRefOpenGL = 0;

// -------------------------

OSStatus aglInitEntryPoints (void)
{
    OSStatus err = noErr;
    const Str255 frameworkName = "\pOpenGL.framework";
    FSRefParam fileRefParam;
    FSRef fileRef;
    CFURLRef bundleURLOpenGL;

    memset(&fileRefParam, 0, sizeof(fileRefParam));
    memset(&fileRef, 0, sizeof(fileRef));

    fileRefParam.ioNamePtr  = frameworkName;
    fileRefParam.newRef = &fileRef;

    // Frameworks directory/folder
    err = FindFolder (kSystemDomain, kFrameworksFolderType, false,
                      &fileRefParam.ioVRefNum, &fileRefParam.ioDirID);
    if (noErr != err) {
        DebugStr ("\pCould not find frameworks folder");
        return err;
    }
    err = PBMakeFSRefSync (&fileRefParam); // make FSRef for folder
    if (noErr != err) {
        DebugStr ("\pCould make FSref to frameworks folder");
        return err;
    }
    // create URL to folder
    bundleURLOpenGL = CFURLCreateFromFSRef (kCFAllocatorDefault,
                                            &fileRef);
    if (!bundleURLOpenGL) {
        DebugStr ("\pCould create OpenGL Framework bundle URL");
        return paramErr;
    }
    // create ref to GL's bundle
    gBundleRefOpenGL = CFBundleCreate (kCFAllocatorDefault,
                                       bundleURLOpenGL);
    if (!gBundleRefOpenGL) {
        DebugStr ("\pCould not create OpenGL Framework bundle");
        return paramErr;
    }
    CFRelease (bundleURLOpenGL); // release created bundle
    // if the code was successfully loaded, look for our function.
    if (!CFBundleLoadExecutable (gBundleRefOpenGL)) {
        DebugStr ("\pCould not load MachO executable");
        return paramErr;
    }
    return err;
}

// -------------------------

void aglDellocEntryPoints (void)
{
    if (gBundleRefOpenGL != NULL) {
        // unload the bundle's code.
        CFBundleUnloadExecutable (gBundleRefOpenGL);
        CFRelease (gBundleRefOpenGL);
        gBundleRefOpenGL = NULL;
    }
}

// -------------------------

void * aglGetProcAddress (char * pszProc)
{
    return CFBundleGetFunctionPointerForName (gBundleRefOpenGL,
                CFStringCreateWithCStringNoCopy (NULL,
                     pszProc, CFStringGetSystemEncoding (), NULL));
}

Creator::Creator()
{
	if( hardwares )
		throw Gluck(__FUNCTION__, "Hardwares already exists");
		
	OSStatus err = aglInitEntryPoints();

	hardwares = new std::vector<Hardware *>;

	Hardware * ha = new Hardware();
	hardwares->push_back( ha );
	hard::Manager::add_hardware( ha );
}

hard::Hardware * Creator::get_hardware()const
{
	return hardwares->front();
}

Creator::~Creator()
{
	while( !hardwares->empty() )
	{
		Manager::remove_hardware( hardwares->back() );
		delete hardwares->back();
		hardwares->pop_back();
	}

	logs::log() << "Deleting hardwares\n";
	delete hardwares; hardwares = 0;
	
	aglDellocEntryPoints();
}

void * Creator::get_proc_address( const char * proc_name )
{
	return aglGetProcAddress( const_cast<char *>(proc_name) );
}

} // namespace macx
} // namespace hard
} // namespace lib3d
} // namespace liba

